#include<Util.h>

namespace muduo{
    std::unordered_map<int,std::string> _statu_msg={
        {100,  "Continue"},
        {101,  "Switching Protocol"},
        {102,  "Processing"},
        {103,  "Early Hints"},
        {200,  "OK"},
        {201,  "Created"},
        {202,  "Accepted"},
        {203,  "Non-Authoritative Information"},
        {204,  "No Content"},
        {205,  "Reset Content"},
        {206,  "Partial Content"},
        {207,  "Multi-Status"},
        {208,  "Already Reported"},
        {226,  "IM Used"},
        {300,  "Multiple Choice"},
        {301,  "Moved Permanently"},
        {302,  "Found"},
        {303,  "See Other"},
        {304,  "Not Modified"},
        {305,  "Use Proxy"},
        {306,  "unused"},
        {307,  "Temporary Redirect"},
        {308,  "Permanent Redirect"},
        {400,  "Bad Request"},
        {401,  "Unauthorized"},
        {402,  "Payment Required"},
        {403,  "Forbidden"},
        {404,  "Not Found"},
        {405,  "Method Not Allowed"},
        {406,  "Not Acceptable"},
        {407,  "Proxy Authentication Required"},
        {408,  "Request Timeout"},
        {409,  "Conflict"},
        {410,  "Gone"},
        {411,  "Length Required"},
        {412,  "Precondition Failed"},
        {413,  "Payload Too Large"},
        {414,  "URI Too Long"},
        {415,  "Unsupported Media Type"},
        {416,  "Range Not Satisfiable"},
        {417,  "Expectation Failed"},
        {418,  "I'm a teapot"},
        {421,  "Misdirected Request"},
        {422,  "Unprocessable Entity"},
        {423,  "Locked"},
        {424,  "Failed Dependency"},
        {425,  "Too Early"},
        {426,  "Upgrade Required"},
        {428,  "Precondition Required"},
        {429,  "Too Many Requests"},
        {431,  "Request Header Fields Too Large"},
        {451,  "Unavailable For Legal Reasons"},
        {501,  "Not Implemented"},
        {502,  "Bad Gateway"},
        {503,  "Service Unavailable"},
        {504,  "Gateway Timeout"},
        {505,  "HTTP Version Not Supported"},
        {506,  "Variant Also Negotiates"},
        {507,  "Insufficient Storage"},
        {508,  "Loop Detected"},
        {510,  "Not Extended"},
        {511,  "Network Authentication Required"},
    };
    std::unordered_map<std::string,std::string> _mime_msg = {
        {".aac",        "audio/aac"},
        {".abw",        "application/x-abiword"},
        {".arc",        "application/x-freearc"},
        {".avi",        "video/x-msvideo"},
        {".azw",        "application/vnd.amazon.ebook"},
        {".bin",        "application/octet-stream"},
        {".bmp",        "image/bmp"},
        {".bz",         "application/x-bzip"},
        {".bz2",        "application/x-bzip2"},
        {".csh",        "application/x-csh"},
        {".css",        "text/css"},
        {".csv",        "text/csv"},
        {".doc",        "application/msword"},
        {".docx",       "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
        {".eot",        "application/vnd.ms-fontobject"},
        {".epub",       "application/epub+zip"},
        {".gif",        "image/gif"},
        {".htm",        "text/html"},
        {".html",       "text/html"},
        {".ico",        "image/vnd.microsoft.icon"},
        {".ics",        "text/calendar"},
        {".jar",        "application/java-archive"},
        {".jpeg",       "image/jpeg"},
        {".jpg",        "image/jpeg"},
        {".js",         "text/javascript"},
        {".json",       "application/json"},
        {".jsonld",     "application/ld+json"},
        {".mid",        "audio/midi"},
        {".midi",       "audio/x-midi"},
        {".mjs",        "text/javascript"},
        {".mp3",        "audio/mpeg"},
        {".mpeg",       "video/mpeg"},
        {".mpkg",       "application/vnd.apple.installer+xml"},
        {".odp",        "application/vnd.oasis.opendocument.presentation"},
        {".ods",        "application/vnd.oasis.opendocument.spreadsheet"},
        {".odt",        "application/vnd.oasis.opendocument.text"},
        {".oga",        "audio/ogg"},
        {".ogv",        "video/ogg"},
        {".ogx",        "application/ogg"},
        {".otf",        "font/otf"},
        {".png",        "image/png"},
        {".pdf",        "application/pdf"},
        {".ppt",        "application/vnd.ms-powerpoint"},
        {".pptx",       "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
        {".rar",        "application/x-rar-compressed"},
        {".rtf",        "application/rtf"},
        {".sh",         "application/x-sh"},
        {".svg",        "image/svg+xml"},
        {".swf",        "application/x-shockwave-flash"},
        {".tar",        "application/x-tar"},
        {".tif",        "image/tiff"},
        {".tiff",       "image/tiff"},
        {".ttf",        "font/ttf"},
        {".txt",        "text/plain"},
        {".vsd",        "application/vnd.visio"},
        {".wav",        "audio/wav"},
        {".weba",       "audio/webm"},
        {".webm",       "video/webm"},
        {".webp",       "image/webp"},
        {".woff",       "font/woff"},
        {".woff2",      "font/woff2"},
        {".xhtml",      "application/xhtml+xml"},
        {".xls",        "application/vnd.ms-excel"},
        {".xlsx",       "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
        {".xml",        "application/xml"},
        {".xul",        "application/vnd.mozilla.xul+xml"},
        {".zip",        "application/zip"},
        {".3gp",        "video/3gpp"},
        {".3g2",        "video/3gpp2"},
        {".7z",         "application/x-7z-compressed"},
    };
        // 分割字符串，按照sep进行分割，将得到的字串放到array中，返回字符串数量
        // a,,b,c
        size_t Util::Split(const std::string& src,const std::string sep,std::vector<std::string>* array){
            int pos = 0,tmp=0;
            while(tmp < src.size()){
                pos=src.find(sep,tmp);
                // 说明找到末尾都没找到，直接放进array中
                if(pos == std::string::npos){
                    array->push_back(src.substr(tmp));
                    return array->size();
                }
                if(pos!=tmp){
                    array->push_back(src.substr(tmp,pos-tmp));
                }
                tmp = pos+sep.size();
            }
            return array->size();
        }
        
        // 读取文件的所有内容，放到一个string中
        bool Util::ReadFile(const std::string& filename,std::string* buffer){
            // 将文件以二进制形式进行读取
            std::ifstream ifs(filename,std::ios::binary);
            if(ifs.is_open() == false){
                std::cout << "OPEN FILE: " << filename << " FAILED!" << std::endl; 
                return false;
            }
            size_t fsize =0 ;
            // 跳转读写位置到末尾
            ifs.seekg(0,ifs.end);
            // 获取当前读写位置相对于起始位置的偏移量，这个偏移量也就是文件大小
            fsize = ifs.tellg();
            // 再跳转到起始位置
            ifs.seekg(0,ifs.beg);
            // 开辟文件大小空间
            buffer->resize(fsize);
            // 将数据移入buffer中
            ifs.read(&(*buffer)[0], fsize);
            if(ifs.good()==false){
                std::cout << "READ FILE: " << filename << " FAILED!" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        // 向文件中写入数据
        bool Util::WriteFile(const std::string& filename,const std::string& buffer){
            // 一个std::ofstream输出文件流的对象，表示可以通过ofs对象向文件写入，binary以二进制模式打开文件，且写入时不做任何格式转换
            // trunc表示,存在文件则内容清空写入,不存在则报错
            std::ofstream ofs(filename,std::ios::binary|std::ios::trunc);
            if(ofs.is_open()==false){
                std::cout << "OPEN FILE: " << filename << " FAILED!" << std::endl; 
                return false;
            }
            ofs.write(buffer.c_str(), buffer.size());
            if(ofs.good()==false){
                std::cout << "WRITE FILE: " << filename << " FAILED!" << std::endl;
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }
        // URL编码：避免url中的特殊字符与被查询字符串的特殊字符与http请求特殊字符产生歧义
        // 把url中可能引发歧义的字符串转化为一种特殊的格式，确保这些字符在url中被安全的传递
        // 编码格式，将特殊字符的ascii码值，转换为两个16进制字符，前缀加%号，：C++ -> C%2B%2B
        // 不编码的特殊字符，RFC3986文档规定 . _ - ~，字母、数字属于绝对不编码字符
        // RFC3986文档规定,编码格式为%HH,就是%后加两个大写16进制字符
        // W3C标准中规定，查询字符串中的空格，需要编码为+， 解码则是+转空格
        std::string Util::UrlEncode(const std::string& url,bool convert_space_to_plus){
            std::string ret;
            for(auto c:url){
                // isalnum判断是否是字母或是数字
                if(c=='.' || c == '_' || c == '-' || c == '~' || isalnum(c)){
                    ret += c;
                    continue;
                }
                if(convert_space_to_plus && c == ' '){
                    ret += '+';
                    continue;
                }
                // %HH占三个字符，snprintf最后要加'\0'
                char tmp[4] = {0};
                // %是特殊字符，所以需要%%转义为%，%02X，表示两位16进制数字，不够两位则补0
                snprintf(tmp,sizeof(tmp), "%%%02X", c);
                ret+=tmp;
            }
            return ret;
        }
        char Util::HEXTOI(char c){
            if(c >= '0' && c <= '9'){
                return c-'0' ;
            }else if(c >= 'a' && c <= 'z'){
                return c - 'a'+ 10;
            }
            else if(c >= 'A' && c<='Z'){
                return c - 'A'+10;
            }
            return -1;
        }
        // URL解码: 将编码后的url编码解码为原来的字符串
        std::string Util::UrlDecode(const std::string& urlencode,bool convert_plus_to_space){
            std::string ret;
            for(int i=0;i<urlencode.size();i++){
                if(urlencode[i] == '+' && convert_plus_to_space){
                    ret += ' ';
                    continue;
                }
                if(urlencode[i] == '%' && (i+2) <urlencode.size()){
                    char c1 = HEXTOI(urlencode[i+1]);
                    char c2 = HEXTOI(urlencode[i+2]);
                    char c=c1*16+c2;
                    i+=2;
                    ret += c;
                    continue;
                }
                ret +=urlencode[i];
            }
            return ret;
        }
        // 响应状态码的描述信息
        std::string Util::StatuDesc(int status){
            auto iter = _statu_msg.find(status);
            if(iter == _statu_msg.end()){
                return "UNKNOWN STATUS";
            }
            return iter->second;
        }
        // 根据文件名后缀获取mime
        std::string Util::Getmime(const std::string& filename){
            int pos = filename.rfind('.');
            std::string str = filename.substr(pos);
            auto iter = _mime_msg.find(str);
            if(iter == _mime_msg.end()){
                return "UNKNOWN MIME";
            }
            return iter->second;
        }
        // 判断文件是否是一个目录
        bool Util::IsDirectory(const std::string& filename){
            // stat结构体定义了文件的各种属性，如大小权限类型等
            struct stat st;
            // stat获取文件元数据，也就是属性
            int ret = stat(filename.c_str(),&st);
            if(ret < 0){
                return false;
            }
            // 是一个宏，来判断这个文件模式是否是目录
            return S_ISDIR(st.st_mode);
        }
        // 判断一个文件是否是一个普通文件
        bool Util::IsRegular(const std::string& filename){
             // stat结构体定义了文件的各种属性，如大小权限类型等
             struct stat st;
             // stat获取文件元数据，也就是属性
             int ret = stat(filename.c_str(),&st);
             if(ret < 0){
                 return false;
             }
             // 是一个宏，来判断这个文件模式是否是目录
             return S_ISREG(st.st_mode);
        }
        // 判断访问路径的合法性
        // /../name不合法 只有在访问服务器根目录下的文件才合法
        bool Util::ValidPath(const std::string& path){
            int count = 0;
            // 用来存储每次截取的字符串内容
            std::vector<std::string> subpath ;
            Split(path, "/", &subpath);
            for(auto str: subpath){
                if(str == ".."){
                    count--;
                    if(count <0){
                        return false;
                    }
                    continue;
                }
                count++;
            }
            return true;
        }
}